From 131b6434b649aff6b2c36c3215cf0c52055a53ae Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Thu, 4 Nov 2021 08:02:09 -0600 Subject: [PATCH] optimize speed and distance conversions. (#753) * optimize speed and distance conversions. structure constexpr conversions such that a minimal amount of arithmetic is done at run time and avoiding run time division. add tests for conversions. * correct bug with display of accumulated path distance. and add test case of same. --- defs.h | 45 ++++++++++++++---------- reference/distance.csv | 7 ++++ reference/distance2.csv | 2 ++ reference/distance2~csv.csv | 2 ++ reference/distance3.csv | 3 ++ reference/distance3~csv.csv | 3 ++ reference/distance~csv.csv | 7 ++++ reference/speed.csv | 6 ++++ reference/speed~csv.csv | 5 +++ testo.d/unitconversion.test | 68 +++++++++++++++++++++++++++++++++++++ xcsv.cc | 24 +++++++++---- xcsv.h | 1 + xmldoc/chapters/styles.xml | 6 ++++ 13 files changed, 155 insertions(+), 24 deletions(-) create mode 100644 reference/distance.csv create mode 100644 reference/distance2.csv create mode 100644 reference/distance2~csv.csv create mode 100644 reference/distance3.csv create mode 100644 reference/distance3~csv.csv create mode 100644 reference/distance~csv.csv create mode 100644 reference/speed.csv create mode 100644 reference/speed~csv.csv create mode 100644 testo.d/unitconversion.test diff --git a/defs.h b/defs.h index 70ee1262a..68c14159d 100644 --- a/defs.h +++ b/defs.h @@ -77,40 +77,49 @@ constexpr double kMetersPerMile = 1609.344; /* exact in decimal notation */ constexpr double kMilesPerMeter = 1.0 / kMetersPerMile; constexpr double kKilometersPerMile = 1.609344; /* exact in decimal notation */ constexpr double kMilesPerKilometer = 1.0 / kKilometersPerMile; +constexpr double kMetersPerNMile = 1852.0; /* exact in decimal notation */ +constexpr double kNMilesPerMeter = 1.0 / kMetersPerNMile; -constexpr double FEET_TO_METERS(double feetsies) { return (feetsies) * kMetersPerFoot; } -constexpr double METERS_TO_FEET(double meetsies) { return (meetsies) * kFeetPerMeter; } +constexpr double FEET_TO_METERS(double feetsies) { return feetsies * kMetersPerFoot;} +constexpr double METERS_TO_FEET(double meetsies) { return meetsies * kFeetPerMeter;} -constexpr double NMILES_TO_METERS(double a) { return a * 1852.0;} /* nautical miles */ -constexpr double METERS_TO_NMILES(double a) { return a / 1852.0;} +constexpr double NMILES_TO_METERS(double a) { return a * kMetersPerNMile;} /* nautical miles */ +constexpr double METERS_TO_NMILES(double a) { return a * kNMilesPerMeter;} -constexpr double MILES_TO_METERS(double a) { return (a) * kMetersPerMile;} -constexpr double METERS_TO_MILES(double a) { return (a) * kMilesPerMeter;} -constexpr double FATHOMS_TO_METERS(double a) { return (a) * 1.8288;} +constexpr double MILES_TO_METERS(double a) { return a * kMetersPerMile;} +constexpr double METERS_TO_MILES(double a) { return a * kMilesPerMeter;} +constexpr double FATHOMS_TO_METERS(double a) { return a * 1.8288;} -constexpr double CELSIUS_TO_FAHRENHEIT(double a) { return (((a) * 1.8) + 32.0);} -constexpr double FAHRENHEIT_TO_CELSIUS(double a) { return (((a) - 32.0) / 1.8);} +constexpr double CELSIUS_TO_FAHRENHEIT(double a) { return (a * 1.8) + 32.0;} +constexpr double FAHRENHEIT_TO_CELSIUS(double a) { return (a - 32.0) / 1.8;} constexpr long SECONDS_PER_HOUR = 60L * 60; constexpr long SECONDS_PER_DAY = 24L * 60 * 60; +constexpr double kKPHPerMPS = SECONDS_PER_HOUR / 1000.0; +constexpr double kMPSPerKPH = 1.0 / kKPHPerMPS; +constexpr double kMPHPerMPS = kMilesPerMeter * SECONDS_PER_HOUR; +constexpr double kMPSPerMPH = 1.0 / kMPHPerMPS; +constexpr double kKnotsPerMPS = kNMilesPerMeter * SECONDS_PER_HOUR; +constexpr double kMPSPerKnot = 1.0 / kKnotsPerMPS; + /* meters/second to kilometers/hour */ -constexpr double MPS_TO_KPH(double a) { return (a)*SECONDS_PER_HOUR/1000.0;} +constexpr double MPS_TO_KPH(double a) { return a * kKPHPerMPS;} /* meters/second to miles/hour */ -constexpr double MPS_TO_MPH(double a) { return METERS_TO_MILES(a) * SECONDS_PER_HOUR;} +constexpr double MPS_TO_MPH(double a) { return a * kMPHPerMPS;} -/* meters/second to knots */ -constexpr double MPS_TO_KNOTS(double a) { return MPS_TO_KPH((a)/1.852);} +/* meters/second to knots(nautical miles/hour) */ +constexpr double MPS_TO_KNOTS(double a) { return a * kKnotsPerMPS;} /* kilometers/hour to meters/second */ -constexpr double KPH_TO_MPS(double a) { return a * 1000.0/SECONDS_PER_HOUR;} +constexpr double KPH_TO_MPS(double a) { return a * kMPSPerKPH;} /* miles/hour to meters/second */ -#define MPH_TO_MPS(a) (MILES_TO_METERS(a) / SECONDS_PER_HOUR) +constexpr double MPH_TO_MPS(double a) { return a * kMPSPerMPH;} -/* knots to meters/second */ -constexpr double KNOTS_TO_MPS(double a) {return KPH_TO_MPS(a) * 1.852; } +/* knots(nautical miles/hour) to meters/second */ +constexpr double KNOTS_TO_MPS(double a) {return a * kMPSPerKnot;} #define MILLI_TO_MICRO(t) ((t) * 1000) /* Milliseconds to Microseconds */ #define MICRO_TO_MILLI(t) ((t) / 1000) /* Microseconds to Milliseconds*/ @@ -518,7 +527,7 @@ public: unsigned char cadence; /* revolutions per minute */ float power; /* watts, as measured by cyclists */ float temperature; /* Degrees celsius */ - float odometer_distance; /* Meters? */ + float odometer_distance; /* Meters */ geocache_data* gc_data; FormatSpecificDataList fs; const session_t* session; /* pointer to a session struct */ diff --git a/reference/distance.csv b/reference/distance.csv new file mode 100644 index 000000000..f51d3dbad --- /dev/null +++ b/reference/distance.csv @@ -0,0 +1,7 @@ +lat,lon,alt +40.0,-105.0,1.0m +40.0,-105.0,1.0ft +40.0,-105.0,1.0km +40.0,-105.0,1.0nm +40.0,-105.0,1.0mi +40.0,-105.0,1.0fa diff --git a/reference/distance2.csv b/reference/distance2.csv new file mode 100644 index 000000000..0a5721192 --- /dev/null +++ b/reference/distance2.csv @@ -0,0 +1,2 @@ +Lat,Lon,Dist(m) +40.000000,-105.000000,1.0 diff --git a/reference/distance2~csv.csv b/reference/distance2~csv.csv new file mode 100644 index 000000000..85ef25a7b --- /dev/null +++ b/reference/distance2~csv.csv @@ -0,0 +1,2 @@ +Lat,Lon,Dist(m),Dist(mile),Dist(nautical miles) +40.000000,-105.000000,1.000000e+00,6.213712e-04,5.399568e-04 diff --git a/reference/distance3.csv b/reference/distance3.csv new file mode 100644 index 000000000..a8598c914 --- /dev/null +++ b/reference/distance3.csv @@ -0,0 +1,3 @@ +UTM +21N 499500 0 +21N 500500 0 diff --git a/reference/distance3~csv.csv b/reference/distance3~csv.csv new file mode 100644 index 000000000..bceb24f47 --- /dev/null +++ b/reference/distance3~csv.csv @@ -0,0 +1,3 @@ +Utm,Dist(m),Dist(km),Dist(mile),Dist(nautical miles) +21N 499500 0,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00 +21N 500500 0,1.000400e+03,1.000400e+00,6.216198e-01,5.401729e-01 diff --git a/reference/distance~csv.csv b/reference/distance~csv.csv new file mode 100644 index 000000000..26bd96c40 --- /dev/null +++ b/reference/distance~csv.csv @@ -0,0 +1,7 @@ +Lat,Lon,Alt(m),Alt(ft) +40.000000,-105.000000,1.000000e+00,3.280840e+00 +40.000000,-105.000000,3.048000e-01,1.000000e+00 +40.000000,-105.000000,1.000000e+03,3.280840e+03 +40.000000,-105.000000,1.852000e+03,6.076115e+03 +40.000000,-105.000000,1.609344e+03,5.280000e+03 +40.000000,-105.000000,1.828800e+00,6.000000e+00 diff --git a/reference/speed.csv b/reference/speed.csv new file mode 100644 index 000000000..27a62d2c1 --- /dev/null +++ b/reference/speed.csv @@ -0,0 +1,6 @@ +lat,lon,speed +40.0,-105.0,1.0mps +40.0,-105.0,1.0kmh +40.0,-105.0,1.0mph +40.0,-105.0,1.0knot + diff --git a/reference/speed~csv.csv b/reference/speed~csv.csv new file mode 100644 index 000000000..4d62e6b67 --- /dev/null +++ b/reference/speed~csv.csv @@ -0,0 +1,5 @@ +Lat,Lon,Speed(m/s),Speed(km/hr),Speed(mile/hr),Speed(knots) +40.000000,-105.000000,1.000000e+00,3.600000e+00,2.236936e+00,1.943844e+00 +40.000000,-105.000000,2.777778e-01,1.000000e+00,6.213712e-01,5.399568e-01 +40.000000,-105.000000,4.470400e-01,1.609344e+00,1.000000e+00,8.689762e-01 +40.000000,-105.000000,5.144445e-01,1.852000e+00,1.150780e+00,1.000000e+00 diff --git a/testo.d/unitconversion.test b/testo.d/unitconversion.test new file mode 100644 index 000000000..55fb27f7a --- /dev/null +++ b/testo.d/unitconversion.test @@ -0,0 +1,68 @@ +# This covers conversions to and from various speed units. +echo 'DESCRIPTION Speed Test' > speed.style +echo 'EXTENSION csv' >> speed.style +echo 'FIELD_DELIMITER COMMA' >> speed.style +echo 'RECORD_DELIMITER NEWLINE' >> speed.style +echo 'DATATYPE TRACK' >> speed.style +echo 'PROLOGUE Lat,Lon,Speed(m/s),Speed(km/hr),Speed(mile/hr),Speed(knots)' >> speed.style +echo 'OFIELD LAT_DECIMAL,"","%f"' >> speed.style +echo 'OFIELD LON_DECIMAL,"","%f"' >> speed.style +echo 'OFIELD PATH_SPEED,"","%.6e"' >> speed.style +echo 'OFIELD PATH_SPEED_KPH,"","%.6e"' >> speed.style +echo 'OFIELD PATH_SPEED_MPH,"","%.6e"' >> speed.style +echo 'OFIELD PATH_SPEED_KNOTS,"","%.6e"' >> speed.style + +gpsbabel -t -i unicsv -f ${REFERENCE}/speed.csv -o xcsv,style=speed.style -F ${TMPDIR}/speed~csv.csv +compare ${REFERENCE}/speed~csv.csv ${TMPDIR}/speed~csv.csv + +# This covers distance conversions to meters, but only a couple from meters. +echo 'DESCRIPTION Distance Test' > distance.style +echo 'EXTENSION csv' >> distance.style +echo 'FIELD_DELIMITER COMMA' >> distance.style +echo 'RECORD_DELIMITER NEWLINE' >> distance.style +echo 'DATATYPE TRACK' >> distance.style +echo 'PROLOGUE Lat,Lon,Alt(m),Alt(ft)' >> distance.style +echo 'OFIELD LAT_DECIMAL,"","%f"' >> distance.style +echo 'OFIELD LON_DECIMAL,"","%f"' >> distance.style +echo 'OFIELD ALT_METERS,"","%.6e"' >> distance.style +echo 'OFIELD ALT_FEET,"","%.6e"' >> distance.style + +gpsbabel -t -i unicsv -f ${REFERENCE}/distance.csv -o xcsv,style=distance.style -F ${TMPDIR}/distance~csv.csv +compare ${REFERENCE}/distance~csv.csv ${TMPDIR}/distance~csv.csv + +# This covers additional distance conversions from meters. +echo 'DESCRIPTION Distance 2 Test' > distance2.style +echo 'EXTENSION csv' >> distance2.style +echo 'FIELD_DELIMITER COMMA' >> distance2.style +echo 'RECORD_DELIMITER NEWLINE' >> distance2.style +echo 'DATATYPE TRACK' >> distance2.style +echo 'PROLOGUE Lat,Lon,Dist(m),Dist(mile),Dist(nautical miles)' >> distance2.style +echo 'IFIELD LAT_DECIMAL,"","%f"' >> distance2.style +echo 'IFIELD LON_DECIMAL,"","%f"' >> distance2.style +echo 'IFIELD PATH_DISTANCE_METERS,"","%.6e"' >> distance2.style +echo 'OFIELD LAT_DECIMAL,"","%f"' >> distance2.style +echo 'OFIELD LON_DECIMAL,"","%f"' >> distance2.style +echo 'OFIELD PATH_DISTANCE_METERS,"","%.6e"' >> distance2.style +echo 'OFIELD PATH_DISTANCE_MILES,"","%.6e"' >> distance2.style +echo 'OFIELD PATH_DISTANCE_NAUTICAL_MILES,"","%.6e"' >> distance2.style + +gpsbabel -t -i xcsv,style=distance2.style -f ${REFERENCE}/distance2.csv -o xcsv,style=distance2.style -F ${TMPDIR}/distance2~csv.csv +compare ${REFERENCE}/distance2~csv.csv ${TMPDIR}/distance2~csv.csv + +# This covers xcsv path distances. +# Reference has points ~1000m apart. +echo 'DESCRIPTION Distance 3 Test' > distance3.style +echo 'EXTENSION csv' >> distance3.style +echo 'FIELD_DELIMITER COMMA' >> distance3.style +echo 'RECORD_DELIMITER NEWLINE' >> distance3.style +echo 'DATATYPE TRACK' >> distance3.style +echo 'PROLOGUE Utm,Dist(m),Dist(km),Dist(mile),Dist(nautical miles)' >> distance3.style +echo 'IFIELD UTM,"","%s"' >> distance3.style +echo 'OFIELD UTM,"","%s"' >> distance3.style +echo 'OFIELD PATH_DISTANCE_METERS,"","%.6e"' >> distance3.style +echo 'OFIELD PATH_DISTANCE_KM,"","%.6e"' >> distance3.style +echo 'OFIELD PATH_DISTANCE_MILES,"","%.6e"' >> distance3.style +echo 'OFIELD PATH_DISTANCE_NAUTICAL_MILES,"","%.6e"' >> distance3.style + +gpsbabel -t -i xcsv,style=distance3.style -f ${REFERENCE}/distance3.csv -o xcsv,style=distance3.style -F ${TMPDIR}/distance3~csv.csv +compare ${REFERENCE}/distance3~csv.csv ${TMPDIR}/distance3~csv.csv diff --git a/xcsv.cc b/xcsv.cc index 4114018ee..02eb1ab9c 100644 --- a/xcsv.cc +++ b/xcsv.cc @@ -51,7 +51,7 @@ #include "formspec.h" // for FormatSpecificDataList #include "garmin_fs.h" // for garmin_fs_t, garmin_fs_alloc #include "gbfile.h" // for gbfgetstr, gbfclose, gbfopen, gbfile -#include "grtcirc.h" // for RAD, gcdist, radtomiles +#include "grtcirc.h" // for RAD, gcdist, radtometers #include "jeeps/gpsmath.h" // for GPS_Math_WGS84_To_UTM_EN, GPS_Lookup_Datum_Index, GPS_Math_Known_Datum_To_WGS84_M, GPS_Math_UTM_EN_To_Known_Datum, GPS_Math_WGS84_To_Known_Datum_M, GPS_Math_WGS84_To_UKOSMap_M #include "jeeps/gpsport.h" // for int32 #include "session.h" // for session_t @@ -128,6 +128,7 @@ const QHash XcsvStyle::xcsv_tokens { { "PATH_DISTANCE_KM", XT_PATH_DISTANCE_KM }, { "PATH_DISTANCE_METERS", XT_PATH_DISTANCE_METERS }, { "PATH_DISTANCE_MILES", XT_PATH_DISTANCE_MILES }, + { "PATH_DISTANCE_NAUTICAL_MILES", XT_PATH_DISTANCE_NAUTICAL_MILES }, { "PATH_SPEED", XT_PATH_SPEED }, { "PATH_SPEED_KNOTS", XT_PATH_SPEED_KNOTS }, { "PATH_SPEED_KPH", XT_PATH_SPEED_KPH }, @@ -751,6 +752,9 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle: case XcsvStyle::XT_PATH_DISTANCE_MILES: wpt->odometer_distance = MILES_TO_METERS(atof(s)); break; + case XcsvStyle::XT_PATH_DISTANCE_NAUTICAL_MILES: + wpt->odometer_distance = NMILES_TO_METERS(atof(s)); + break; case XcsvStyle::XT_HEART_RATE: wpt->heartrate = atoi(s); break; @@ -981,8 +985,8 @@ XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt) char utmzc; if (oldlon < 900) { - pathdist += radtomiles(gcdist(RAD(oldlat),RAD(oldlon), - RAD(wpt->latitude),RAD(wpt->longitude))); + pathdist += radtometers(gcdist(RAD(oldlat),RAD(oldlon), + RAD(wpt->latitude),RAD(wpt->longitude))); } longitude = oldlon = wpt->longitude; latitude = oldlat = wpt->latitude; @@ -1282,7 +1286,15 @@ XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt) if (wpt->odometer_distance) { buff = QString::asprintf(fmp.printfc.constData(), METERS_TO_MILES(wpt->odometer_distance)); } else { - buff = QString::asprintf(fmp.printfc.constData(), pathdist); + buff = QString::asprintf(fmp.printfc.constData(), METERS_TO_MILES(pathdist)); + } + break; + case XcsvStyle::XT_PATH_DISTANCE_NAUTICAL_MILES: + /* path (route/track) distance in miles */ + if (wpt->odometer_distance) { + buff = QString::asprintf(fmp.printfc.constData(), METERS_TO_NMILES(wpt->odometer_distance)); + } else { + buff = QString::asprintf(fmp.printfc.constData(), METERS_TO_NMILES(pathdist)); } break; case XcsvStyle::XT_PATH_DISTANCE_METERS: @@ -1290,7 +1302,7 @@ XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt) if (wpt->odometer_distance) { buff = QString::asprintf(fmp.printfc.constData(), wpt->odometer_distance); } else { - buff = QString::asprintf(fmp.printfc.constData(), MILES_TO_METERS(pathdist)); + buff = QString::asprintf(fmp.printfc.constData(), pathdist); } break; case XcsvStyle::XT_PATH_DISTANCE_KM: @@ -1298,7 +1310,7 @@ XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt) if (wpt->odometer_distance) { buff = QString::asprintf(fmp.printfc.constData(), wpt->odometer_distance / 1000.0); } else { - buff = QString::asprintf(fmp.printfc.constData(), MILES_TO_METERS(pathdist) / 1000.0); + buff = QString::asprintf(fmp.printfc.constData(), pathdist / 1000.0); } break; case XcsvStyle::XT_PATH_SPEED: diff --git a/xcsv.h b/xcsv.h index cc07b9fad..85f194c06 100644 --- a/xcsv.h +++ b/xcsv.h @@ -118,6 +118,7 @@ public: XT_PATH_DISTANCE_KM, XT_PATH_DISTANCE_METERS, XT_PATH_DISTANCE_MILES, + XT_PATH_DISTANCE_NAUTICAL_MILES, XT_PATH_SPEED, XT_PATH_SPEED_KNOTS, XT_PATH_SPEED_KPH, diff --git a/xmldoc/chapters/styles.xml b/xmldoc/chapters/styles.xml index 1e89332ab..f18b910d8 100644 --- a/xmldoc/chapters/styles.xml +++ b/xmldoc/chapters/styles.xml @@ -1041,6 +1041,12 @@ longitude) PATH_DISTANCE_MILES,"","%f" + +
+ PATH_DISTANCE_NAUTICAL_MILES + PATH_DISTANCE_NAUTICAL_MILES is like PATH_DISTANCE_MILES except it outputs the + length in nautical miles. +
PATH_DISTANCE_KM -- 2.30.2